package org.mtmi.ui.controls;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.swt.graphics.GC;
import org.mtmi.tuio.listeners.DragListener;
import org.mtmi.tuio.listeners.SingleTapListener;
import org.mtmi.tuio.listeners.TUIODragEvent;
import org.mtmi.tuio.listeners.TUIOTapEvent;
import org.mtmi.ui.M;
import org.mtmi.ui.controls.listeners.ControlTappedListener;
import org.mtmi.ui.controls.listeners.TapEvent;
import org.mtmi.ui.controls.listeners.ToggleEvent;
import org.mtmi.ui.controls.listeners.ToggleTapListener;
import org.mtmi.ui.scenes.Scene;

/**
 * Common part for buttons.
 *
 */
public abstract class AbstractButton extends Control 
  implements ITouchableControl, SingleTapListener, DragListener
{
//	protected static final int S_UP=0; //enabled, button is released up
//	protected static final int S_DOWN=1; //enabled, button is pressed 
//	protected static final int S_CHECKED_UP=3; //enabled and toggled, button is released up
//	protected static final int S_CHECKED_DOWN=4; //enabled, button is pressed 
	
	protected boolean isEnabled;
	protected boolean isTapped; //means the user make button down
	protected boolean isChecked; //used for toggle/radio buttons
	/**
	 * Id of the group of radio buttons. Toggle buttons become radio buttons 
	 * if they are set in the same group. Id value must be greater than 0 to be 
	 * seen as a real group id.
	 */
	protected int groupId;
	
	private Set<ControlTappedListener> tapListeners;
	private Set<ToggleTapListener> toggleListeners;
	
	/** M.TOGGLE can be used as flag, otherwise, button is a push one */
	public AbstractButton( Scene scene, int flags ) {
		super( scene, flags );
		isTapped = false;
		isEnabled = true;
		isChecked=false;
	}

	/**
	 * @return the group id of the button, 0 for no group.<br>
	 * Group id is usefull for TOGGLE button only. 
	 */
	public int getGroupId() { return groupId; }
	
	/** change the group id of the button */
	public void setGroupId( int gid ) {
		groupId = gid;
	}
	
	/** @return true if the button is a toggle one */ 
	public boolean isToggleButton() { return (getFlags() & M.TOGGLE)!=0; } 
		
	public boolean isEnabled() {
		return isEnabled;
	}
	
	public void setEnabled( boolean _isEnabled ) {
		isEnabled = _isEnabled;
	}
	
	/** @return true if the toggle button is checked */
	public boolean isChecked() { return isChecked; }
	
	/** change the checked state of toggle button */
	public void setChecked( boolean checked ) {
		isChecked = checked;
	}
	
	
	@Override
	public void tapDown(TUIOTapEvent event) {
		if( isEnabled && !isTapped ) {
			isTapped = true;
			notifyTapListeners( true, event );
			redraw();
		}
	}

	@Override
	public void tapUp(TUIOTapEvent event) {
		if( isEnabled && isTapped ) {
			isTapped = false; 
			notifyTapListeners( false, event );
			if( isToggleButton() ) {
				boolean nchk = !isChecked();
				if( groupId > 0 ) {
					//- actually, this toggle is a radio
					List<ToggleButton> sameGroup = getScene().getButtonsOfSameGroup( groupId );
					if( sameGroup.size() > 1 ) {
						//- this radio is not alone in the group -> acts as a actual radio button
						for ( ToggleButton tb : sameGroup ) {
							if( tb != this ) {
								//- all other buttons than this instance must be released
								if( tb.isChecked() ) {
									tb.setChecked( false );
								}
							} else {
								//- touched button must be pushed
								nchk = true;
							}
						}
					}
				}
				redraw();
				setChecked( nchk );
				notifyToggleTapListeners();
			}
			redraw();
		}
	}
	
	@Override
	public void drag( TUIODragEvent event )
	{
		GC gc = new GC( getScene() );
		try {
			TouchControlEvent e = new TouchControlEvent(  gc, event.ex, event.ey );
			boolean inside = containsTouch( e );
			//drag exit or enter button (isTapped means also currently tap is inside bounds )
			if( isTapped ) {
				if( !inside ) { //exit
					isTapped = false;
					redraw();
				}
			} else {
				if( inside ) { //enter
					isTapped = true;
					redraw();
				}
			}
		} finally {
			gc.dispose();
		}
		
	}
	
	protected void notifyTapListeners( boolean isDown, TUIOTapEvent event) {
		if( tapListeners !=null ) {
			TapEvent e = new TapEvent(this);
			ControlTappedListener lis[] = tapListeners.toArray( new ControlTappedListener[tapListeners.size()] );
			for( ControlTappedListener stl : lis ) {
				if(isDown) {
					stl.controlTappedDown( e );
				} else {
					stl.controlTappedUp( e );
				}
			}
		}
	}
	
	public void addTapListener(ControlTappedListener listener) {
		if( tapListeners==null ) {
			tapListeners = new HashSet<ControlTappedListener>();
		}
		tapListeners.add( listener );
	}
	
	public void removeTapListener(ControlTappedListener listener) {
		tapListeners.remove( listener );
	}

	public void addToggleTapListener(ToggleTapListener l) {
		if( toggleListeners==null ) {
			toggleListeners = new HashSet<ToggleTapListener>();
		}
		toggleListeners.add(l);
	}

	public void removeToggleTapListener(ToggleTapListener l) {
		toggleListeners.remove(l);
	}
	
	private void notifyToggleTapListeners() {
		if( toggleListeners!=null ) {
			ToggleEvent e = new ToggleEvent( this, isChecked() );
			ToggleTapListener lis[] = toggleListeners.toArray( new ToggleTapListener[ toggleListeners.size() ]);
			for( ToggleTapListener tl : lis ) {
				tl.toggleTapped( e );
			}
		}
	}
}
